package com.flute.framework.index;

import java.io.File;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;

import com.flute.framework.FluteMonitor;
import com.flute.framework.StandardDate;
import com.flute.framework.exception.FileLockException;
import com.flute.framework.exception.IndexException;
import com.flute.framework.filesystem.FileLock;
import com.flute.framework.monitor.EventLogger;
import com.flute.framework.monitor.MonitedValue;
import com.flute.framework.util.ftp.LocalFile;
import com.flute.tools.util.FileUtil;

public class IndexerFolder {
	private String location;
	private int backup;
	/**
	 * 索引操作目录
	 */
	private static final String RUNNING = "running";
	/**
	 * 当前最新索引目录
	 */
	private static final String READY = "ready";

	/**
	 * 备份时的读锁
	 */
	private static final String BACKUP = "backup";
	/**
	 * 传输锁
	 */
	private static final String TRANSFER = "transfer";
	/**
	 * 搜索器获取最新索引时的读锁
	 */
	private static final String READ = "read";

	public IndexerFolder() {
		this.backup = 5;
	}

	public IndexerFolder(String location) {
		this.location = location;
		this.backup = 5;
		checkLocation();
	}

	private void checkLocation() {
		if (!location.endsWith("/")) {
			location = location + "/";
		}
	}

	public void recover() throws IndexException, FileLockException {
		info("检查当前索引的有效性...");
		FileLock writelock = new FileLock("write", new LocalFile());
		FileLock tlock = new FileLock(TRANSFER, new LocalFile());
		if (writelock.existed(RUNNING) && tlock.existed(READY)) {
			error("running和ready文件夹数据都失效，请人工核对,程序退出");
			throw new IndexException("running和ready文件夹数据都失效，请人工核对,程序退出");
		} else if (writelock.existed(RUNNING)) {
			info("running中文件损坏，复制ready到running");
			moveReadyToRunning();
		} else if (tlock.existed(READY)) {
			info("ready中文件损坏，复制running到ready");
			moveRunningToReady();
		}
	}

	/**
	 * 检查location下所有的文件夹，除了ready和running以外
	 * 
	 * 所有的文件夹应该都没有锁文件，如果文件夹以时间命名，则认为是备份索引
	 * 
	 * @return
	 */
	public List<StandardDate> getBackupDates() {
		List<StandardDate> list = new ArrayList<StandardDate>();
		File[] files = new File(location).listFiles();
		if (files != null) {
			for (File file : files) {
				if (file.isDirectory() && !file.getName().equals(RUNNING)
						&& !file.getName().equals(READY)) {
					File[] fs = file.listFiles();
					boolean isbackup = true;
					for (File f : fs) {
						if (FileLock.isLockFile(f.getAbsolutePath(),
								new LocalFile())) {
							isbackup = false;
						}
					}
					if (isbackup) {
						try {
							list.add(new StandardDate(file.getName()));
						} catch (ParseException e) {
						}
					}
				}
			}
		}

		return list;
	}

	public void startIndex() throws FileLockException {
	}

	public void endIndex() throws FileLockException {

		info("创建索引传输锁.");

		FileLock lock = new FileLock(TRANSFER, new LocalFile());
		if (!lock.create(getRunningFolder())) {
			error("创建索引传输锁失败.");
			throw new FileLockException("创建索引传输锁失败.");
		} else {

		}

		info("对ready文件夹中索引进行备份...");
		backupReady();

		info("把新建好的索引传输至ready文件夹");
		moveRunningToReady();

		info("删除running文件夹下索引传输锁.");
		lock = new FileLock(TRANSFER, new LocalFile());
		if (!lock.delete(getRunningFolder())) {
			error("删除running索引传输锁失败.");
			throw new FileLockException("删除running索引传输锁失败.");
		}
	}

	public boolean canSearcherRead() {
		if (!new FileLock(TRANSFER, new LocalFile()).existed(getReadyFolder())) {
			File[] files = new File(getReadyFolder()).listFiles();
			if (files != null) {
				for (File file : files) {
					if (!FileLock.isLockFile(file.getAbsolutePath(),
							new LocalFile())) {
						return true;
					}
				}
			}
		}

		return false;
	}

	public FileLock getReadLock() {
		int count = 1;
		FileLock lock = new FileLock(String.valueOf(count) + READ,
				new LocalFile());

		while (true) {
			lock = new FileLock(String.valueOf(count++) + READ, new LocalFile());
			if (!lock.existed(getReadyFolder())) {
				if (lock.create(getReadyFolder())) {
					break;
				}
			}
		}

		return lock;
	}

	private void moveRunningToReady() throws FileLockException {
		info("检查是否有搜索器正在读取ready文件夹.");
		while (readLockExisted()) {
			info("正在读取，等待5秒...");
			doNothing(5000);
		}

		info("清空ready文件夹.");
		FileUtil.clearFolder(getReadyFolder());

		info("在ready文件夹下创建传输锁.");
		FileLock lock = new FileLock(TRANSFER, new LocalFile());
		if (!lock.create(getReadyFolder())) {
			info("在ready文件夹下创建传输锁失败.");
			throw new FileLockException("创建索引传输锁失败.");
		}

		info("复制文件：从running到ready...");
		FileUtil.copyFiles(getRunningFolder(), getReadyFolder());

		info("复制完成，清空ready下所有的锁文件");
		clearLocks(getReadyFolder());
	}

	private void moveReadyToRunning() throws FileLockException {
		info("清空running文件夹.");
		FileUtil.clearFolder(getReadyFolder());

		info("复制文件：从ready到running...");
		FileUtil.copyFiles(getReadyFolder(), getRunningFolder());

		info("复制完成，清空running下所有的锁文件");
		clearLocks(getRunningFolder());
	}

	private void doNothing(long ms) {
		try {
			Thread.sleep(ms);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	private boolean readLockExisted() {
		File[] files = new File(getReadyFolder()).listFiles();
		if (files != null) {
			for (File file : files) {
				if (file.getName().endsWith(READ + "." + FileLock.LOCK)) {
					return true;
				}
			}
		}

		return false;
	}

	private void backupReady() throws FileLockException {
		info("备份ready中的索引...");
		if (!new FileLock(TRANSFER, new LocalFile()).existed(getReadyFolder())) {

			info("在ready文件夹下创建备份锁.");
			FileLock lock = new FileLock(BACKUP, new LocalFile());
			if (!lock.create(getReadyFolder())) {
				error("在ready文件夹创建备份锁失败.");
				throw new FileLockException("在ready文件夹创建备份锁失败.");
			}

			removeOldestFolder();

			String folder = createNewestFolder();

			if (folder != null) {
				lock = new FileLock(BACKUP, new LocalFile());
				if (!lock.create(folder)) {
					error("在备份文件夹创建备份锁失败.");
					throw new FileLockException("在备份文件夹创建备份锁失败.");
				}
				info("复制文件：从ready到备份'" + folder + "'");
				FileUtil.copyFiles(getReadyFolder(), folder);
				info("复制完成，删除所有的锁文件.");
				clearLocks(folder);
			}
		} else {
			error("传输锁存在，正在传输索引到ready文件夹，不能进行备份.");
			throw new FileLockException("传输锁存在，正在传输索引到ready文件夹，不能进行备份.");
		}
	}

	private void clearLocks(String folder) {
		File[] files = new File(folder).listFiles();
		if (files != null) {
			for (File f : files) {
				if (FileLock.isLockFile(f.getAbsolutePath(), new LocalFile())) {
					if(!f.delete()){
						error("删除文件'" + f.getAbsolutePath()+ "'失败");
					}
				}
			}
		}
	}

	private String createNewestFolder() {
		info("创建最新的索引备份文件夹.");
		File[] files = new File(getReadyFolder()).listFiles();
		for (File file : files) {
			if (!FileLock.isLockFile(file.getAbsolutePath(), new LocalFile())) {
				String folderName = new StandardDate(file.lastModified())
						.toSpecialDateString();
				File f = new File(location + folderName);
				int i = 1;
				while (f.exists()) {
					f = new File(location
							+ new StandardDate(file.lastModified() + i++)
									.toSpecialDateString());
				}
				if (!f.mkdir()) {
					info("不能创建备份索引'" + f.getAbsolutePath() + "'.");
				}
				info("创建备份索引文件夹'" + f.getAbsolutePath() + "'.");
				return f.getAbsolutePath() + "/";
			}
		}

		return null;
	}

	private void removeOldestFolder() {
		File[] files = new File(location).listFiles();
		if (files != null) {
			if (files.length >= backup + 2) {
				info("备份数已达到最大，删除最旧的备份.");
				long oldeset = System.currentTimeMillis();
				File file = null;
				for (File f : files) {
					if (oldeset > f.lastModified()) {
						if (!f.getAbsolutePath().endsWith(RUNNING)
								&& !f.getAbsolutePath().endsWith(READY)) {
							file = f;
							oldeset = f.lastModified();
						}
					}
				}

				if (file != null) {
					info("删除备份索引'" + file.getAbsolutePath() + "'");
					FileUtil.delFolder(file.getAbsolutePath());
				} else {
					warn("删除最旧备份索引失败.");
				}
			}
		}
	}

	private void info(String message) {
		EventLogger.info(new MonitedValue(message), EventLogger
				.getCurrentEventId(FluteMonitor.getIndexerKey()));
	}

	private void error(String message) {
		EventLogger.error(new MonitedValue(message), EventLogger
				.getCurrentEventId(FluteMonitor.getIndexerKey()));
	}

	private void warn(String message) {
		EventLogger.warn(new MonitedValue(message), EventLogger
				.getCurrentEventId(FluteMonitor.getIndexerKey()));
	}

	public String getLocation() {
		return location;
	}

	public void setLocation(String location) {
		this.location = location;
		checkLocation();
	}

	public String getRunningFolder() {
		return location + RUNNING + "/";
	}

	public String getReadyFolder() {
		return location + READY + "/";
	}

	public int getBackup() {
		return backup;
	}

	public void setBackup(int backup) {
		this.backup = backup;
	}

}
